﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Runtime.InteropServices;

namespace BufaView
{
	enum BufaType 
	{
		Unknow = -1,
		GPSTrace = 0x05,
		IOStation = 0x0c,
		Service = 0x0d,
		KaoQin = 0x0e,
	}

	class UnknowBufaTypeException : Exception
	{
		public UnknowBufaTypeException(string msg) : base(msg) { }
	}
	
	class BufaHeader
    {
        private byte[] _buff = new byte[12];
		public BufaHeader(byte[] buff, ref int pos)
		{
			int len = buff[pos] | (buff[pos + 1] << 8)
				| (buff[pos + 2] << 16) | (buff[pos + 3] << 24);

			if (len == 76 || len == 73 || len == 67 || len == 55 || len == 63)
			{
				Array.Copy(buff, pos, _buff, 0, 12);
				pos += 12;
			}
			else
			{
				throw new UnknowBufaTypeException(String.Format("invalid data length: {0}, Pos:{1}", (int)len, pos));
			}
		}

		public DateTime CreateTime
		{
			get
			{
				if (_buff == null)
					return new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Local);

				long time = _buff[4] | (_buff[5] << 8) | (_buff[6] << 16) | (_buff[7] << 24);
				TimeSpan ts = TimeSpan.FromSeconds((double)time);

				DateTime dt = new DateTime(1970, 1, 1, 0, 0, 0);

				return dt + ts;
			}
		}

		public int Length
		{
			get
			{
				if (_buff == null)
					return 0;
				int seq = _buff[0] | (_buff[1] << 8) | (_buff[2] << 16) | (_buff[3] << 24);

				return seq;
			}
		}

		public int SequenceNo
		{
			get
			{
				if (_buff == null)
					return -1;
				int seq = _buff[8] | (_buff[9] << 8) | (_buff[10] << 16) | (_buff[11] << 24);

				return seq;
			}
		}
    }

	abstract class BufaBase
	{
		public byte[] Buffer;
		public BufaType ProtocolType
		{
			get
			{
				if (Buffer != null)
				{
					return (BufaType)Buffer[14];
				}
				else
				{
					return BufaType.Unknow;
				}
			}
		}
		public Int32 Length
		{
			get
			{
				if (Buffer != null)
				{
					return Buffer.Length;
				}
				else
				{
					return 0;
				}
			}
		}
		abstract public void InitData(byte[] buff, ref Int32 pos);
	}

	class GPSTrace : BufaBase
	{
		override public void InitData(byte[] buff, ref Int32 pos)
		{
			int len = (buff[pos + 10] << 8 | buff[pos + 11]);
			if (len == 64 || len == 61)
			{
				Buffer = new byte[len];
				Array.Copy(buff, pos, Buffer, 0, len);
				pos += len;
			}
			else
			{
				throw new InvalidDataException(string.Format("Invalid Data Length {0}", len));
			}
		}
	}
	class IOStation : BufaBase
	{
		override public void InitData(byte[] buff, ref Int32 pos)
		{
			int len = (buff[pos + 10] << 8 | buff[pos + 11]);
			if (len == 55)
			{
				Buffer = new byte[len];
				Array.Copy(buff, pos, Buffer, 0, len);
				pos += len;
			}
			else
			{
				throw new InvalidDataException(string.Format("Invalid Data Length {0}", len));
			}
		}
	}
	class Service : BufaBase
	{
		override public void InitData(byte[] buff, ref Int32 pos)
		{
			int len = (buff[pos + 10] << 8 | buff[pos + 11]);
			if (len == 43)
			{
				Buffer = new byte[len];
				Array.Copy(buff, pos, Buffer, 0, len);
				pos += len;
			}
			else
			{
				throw new InvalidDataException(string.Format("Invalid Data Length {0}", len));
			}
		}
	}
	class KaoQin : BufaBase
	{
		override public void InitData(byte[] buff, ref Int32 pos)
		{
			int len = (buff[pos + 10] << 8 | buff[pos + 11]);
			if (len == 51)
			{
				Buffer = new byte[len];
				Array.Copy(buff, pos, Buffer, 0, len);
				pos += len;
			}
			else
			{
				throw new InvalidDataException(string.Format("Invalid Data Length {0}", len));
			}
		}
	}

	class BufaNode
	{
		private BufaHeader	bufaHead;
		private BufaBase node;

		public BufaNode(byte [] buff, ref int pos)
		{
			int pos_bak = pos;
			try
			{
				bufaHead = new BufaHeader(buff, ref pos);
				try
				{
					node = BufaNodeFactory.CreateBufaNode(buff, ref pos);
				}
				catch (System.NullReferenceException ex)
				{

				}
			}
			catch (UnknowBufaTypeException ex)
			{

			}
		}

		public int Length
		{
			get 
			{
				return node == null ? bufaHead.Length : node.Length;
			}
		}

		public BufaType ProtocolType
		{
			get
			{
				return node == null ? BufaType.Unknow : node.ProtocolType;
			}
		}

		public DateTime CreateTime
		{
			get
			{
				return bufaHead.CreateTime;
			}
		}

		public int SequenceNo
		{
			get
			{
				return bufaHead.SequenceNo;
			}
		}

		public string ProtocolData
		{
			get
			{
				if (node == null)
				{
					return "";
				}
				else
				{
					string[] tmp = Array.ConvertAll<byte, string>(node.Buffer, Byte2CharConverter);
					return string.Join(" ", tmp);
				}
			}
		}

		private string Byte2CharConverter(byte b)
		{
			return b.ToString("x02");
		}
	}

	class BufaNodeFactory
	{
		public static BufaBase CreateBufaNode(byte[] _buff, ref Int32 pos)
		{
			string proto_name = "BufaView." + ((BufaType)_buff[pos + 14]).ToString();
			BufaBase obj = (BufaBase)Type.GetType(proto_name).Assembly.CreateInstance(proto_name);
			obj.InitData(_buff, ref pos);
			return obj;
		}
	}

    class BufaFile
    {
		private List<BufaNode> _nodes = null;
        private string _filePath;
        public BufaFile(string filePath)
        {
            _filePath = filePath;
            ParseFile();
        }

        private void ParseFile()
        {
			byte[] buff = File.ReadAllBytes(_filePath);
			int pos = 0;

			if (_nodes == null)
				_nodes = new List<BufaNode>();
			else
				_nodes.Clear();

            while (true)
            {
				BufaNode node = new BufaNode(buff, ref pos);
				_nodes.Add(node);

				if (pos >= buff.Length - 1)
					break;
			}
		}

		public BufaNode this[int idx]
		{
			get
			{
				if (_nodes == null)
				{
					return null;
				}
				else
				{
					return _nodes.ElementAt(idx);
				}
			}
		}

		public List<BufaNode> Nodes
		{
			get
			{
				return _nodes;
			}
		}
    }
}
